package com.runshu.newsmartark.network.okhttpframework.frameworkimpl;


import android.app.Application;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.runshu.newsmartark.base.MyApplication;
import com.runshu.newsmartark.entity.UserInfoEntity;
import com.runshu.newsmartark.network.disklrucache.DiskLruCacheHelper;
import com.runshu.newsmartark.network.netbean.ResponseBean;
import com.runshu.newsmartark.network.netinterface.BaseCallBack;
import com.runshu.newsmartark.network.netinterface.NetFramework;
import com.runshu.newsmartark.network.okhttpframework.data.OKHttpPullRequest;
import com.runshu.newsmartark.util.LodDialogClass;
import com.runshu.newsmartark.util.NetStatusUtils;
import com.runshu.newsmartark.util.common.LogUtil;
import com.runshu.newsmartark.util.y.nativelog.NLogUtil;

import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;


/**
 * Created by Administrator on 2017/7/31.
 */

public class OkHttpFramework implements NetFramework {
    public static final String TAG = "okHttp";
    public static final MediaType toJSONString = MediaType.parse( "application/json;charset=utf-8" );
    private static OkHttpClient okHttpClient;
    private static final int CACHE_SIZE = 1024 * 1024 * 10;//缓存大小为10m
    private static OKHttpPullRequest PULL_REQUEST;//请求数据解析类
    private static Application CONTEXT;
    private static DiskLruCacheHelper mDiskLruCache;
    private static Map<String, Call> sCallMap = new HashMap<>();


    public OkHttpFramework(OKHttpPullRequest okHttpPullRequest) {
        PULL_REQUEST = okHttpPullRequest;
    }

    @Override
    public void init(Application application) {
        CONTEXT = application;
        //日志拦截
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor( new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                LogUtil.w( TAG, "--------->" + message );
            }
        } );
        logging.setLevel( HttpLoggingInterceptor.Level.BODY );
        if (okHttpClient == null) {
            OkHttpClient.Builder builder = getUnsafeOkHttpClient().newBuilder();
            //设置okhttp的拦截策略
            setInterceptStrategy( builder, application );
            //添加缓存策略
            setCacheStrategy( application );
            builder.connectTimeout( 20000, TimeUnit.MILLISECONDS );
            builder.readTimeout( 20000, TimeUnit.MILLISECONDS );
            builder.retryOnConnectionFailure( true ); //自动重连设置为true
            builder.addNetworkInterceptor( logging );
//            builder.addInterceptor(new RetryIntercepter(3)); //重试拦截器2次
            okHttpClient = builder.build();

        }

    }


    @Override
    public void postData(Map<String, Object> data, Map<String, Object> header, String url, final BaseCallBack baseCallBack) {
        //添加请求体
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        /**
         * 创建请求的参数body
         */
        if (data != null) {
            PULL_REQUEST.pullRequestBodyData( data, formBodyBuilder );
        }
        //添加请求的头
        Request.Builder builder = new Request.Builder();
        if (header != null) {
            PULL_REQUEST.pullRequestHeadData( header, builder );
        }
//        builder.addHeader("token","ee3a88c447a14deea1e0522d6433f710");
        RequestBody body = FormBody.create( MediaType.parse( "application/json" ), JSON.toJSONString( data ) );
        builder.post( body ).url( url );
//        builder.post(formBodyBuilder.build()).url(url);
        final Call call = okHttpClient.newCall( builder.build() );
        sCallMap.put( url, call );
        if (!call.isCanceled()) {
            call.enqueue( new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    LogUtil.e( TAG, e.toString() );
                    JSONObject json = new JSONObject();
                    json.put( "url", url );
                    if (data != null)
                        json.put( "data", new Gson().toJson( data ) );
                    json.put( "errormsg", e.getCause() + ":" + e.getMessage() );
                    NLogUtil.e( NLogUtil.Err_NetError, json.toString() );

                    Handler handler = new Handler( Looper.getMainLooper() );
                    //放在UI线程弹Toast
                    //                        ToastUtils.showShort(CONTEXT, "网络出错,请检查网络");
                    handler.post( LodDialogClass::closeCustomCircleProgressDialog );
                    if ("Canceled".equals( e.getMessage() )) {
                        return;
                    }
                    baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
                    sCallMap.remove( getCacheKey( call ) );
                }

                @Override
                public void onResponse(Call call, Response response) {
                    resloveOnResponse( call, response, baseCallBack );
                    response.body().close();
                    response.close();
                    sCallMap.remove( getCacheKey( call ) );
                }
            } );
        }
    }

    @Override
    public void postFile(Map<String, Object> data, Map<String, Object> header, String url, BaseCallBack baseCallBack) {

    }

    private void resloveOnResponse(Call call, Response response, BaseCallBack baseCallBack) {
        String string = "";
        try {
            string = response.body().string();
            LogUtil.i( TAG, "------------------------------>onSuccess" + "  " + string );
        } catch (IOException e) {
            LodDialogClass.closeCustomCircleProgressDialog();
            e.printStackTrace();
        }
        if (response.isSuccessful()) {
            if (!TextUtils.isEmpty( string )) {
                try {
                    pullData( string, baseCallBack );
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        } else {
            LogUtil.e( TAG, "网络出错,请检查网络" );
            Handler handler = new Handler( Looper.getMainLooper() );
            handler.post( () -> {
//                //放在UI线程弹Toast
//                BaseApplication.getInstance().exitAllActivity();
//                ARouterUtils.goActivityNoInterceptor(UserRouter.HOME_LOGIN);
//                ToastUtils.showShort(CONTEXT, "token过期,请重新登录");
                LodDialogClass.closeCustomCircleProgressDialog();
                baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
            } );
        }
    }

    @Override
    public void cachePostData(Map<String, Object> data, Map<String, Object> header, String url, final BaseCallBack
            baseCallBack) {
        //添加请求体
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        if (data != null) {
            PULL_REQUEST.pullRequestBodyData( data, formBodyBuilder );
        }
        //添加请求的头
        Request.Builder builder = new Request.Builder();
        if (header != null) {
            PULL_REQUEST.pullRequestHeadData( header, builder );
        }
//        UserInfoEntity userInfoGlob = MyApplication.getUserInfoGlob();
//        if (userInfoGlob != null) {
//            builder.addHeader( "user_token", userInfoGlob.getId()+"");
//        }
        builder.post( formBodyBuilder.build() ).url( url );
        final Call call = okHttpClient.newCall( builder.build() );
        sCallMap.put( url, call );
        if (!call.isCanceled()) {
            call.enqueue( new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    LogUtil.e( TAG, e.toString() );
                    JSONObject logJson = new JSONObject();
                    logJson.put( "url", url );
                    if (data != null)
                        logJson.put( "data", new Gson().toJson( data ) );
                    logJson.put( "errormsg", e.getCause() + ":" + e.getMessage() );
                    NLogUtil.e( NLogUtil.Err_NetError, logJson.toString() );

                    sCallMap.remove( getCacheKey( call ) );
                    if (NetStatusUtils.isConnected( CONTEXT )) {
                        baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
                    } else {
                        String key = getCacheKey( call );
                        //没有网络的情况下，去获取key对应的缓存
                        String json = null;
                        json = mDiskLruCache.getAsString( key );
                        try {
                            pullData( json, baseCallBack );
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                        LogUtil.e( TAG, "disklrucache:" + json );
                        //如果缓存不存在，那么久回调失败接口
                    }

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    resloveCacheOnResponse( call, response, baseCallBack );
                    response.body().close();
                    response.close();
                    sCallMap.remove( getCacheKey( call ) );
                }
            } );
        }
    }

    @Override
    public void getData(Map<String, Object> data, Map<String, Object> header, String url, BaseCallBack baseCallBack) {
        //添加请求体
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        /**
         * 创建请求的参数body
         */
        if (data != null) {
            PULL_REQUEST.pullRequestBodyData( data, formBodyBuilder );
        }
        //添加请求的头
        Request.Builder builder = new Request.Builder();
        if (header != null) {
            PULL_REQUEST.pullRequestHeadData( header, builder );
        }
        builder.get().url( url );
        final Call call = okHttpClient.newCall( builder.build() );
        sCallMap.put( url, call );
        if (!call.isCanceled()) {
            call.enqueue( new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    LogUtil.e( TAG, e.toString() );

                    JSONObject json = new JSONObject();
                    json.put( "url", url );
                    if (data != null)
                        json.put( "data", new Gson().toJson( data ) );
                    json.put( "errormsg", e.getCause() + ":" + e.getMessage() );
                    NLogUtil.e( NLogUtil.Err_NetError, json.toString() );

                    Handler handler = new Handler( Looper.getMainLooper() );
                    //放在UI线程弹Toast
                    //                        ToastUtils.showShort(CONTEXT, "网络出错,请检查网络");
                    handler.post( LodDialogClass::closeCustomCircleProgressDialog );
                    if ("Canceled".equals( e.getMessage() )) {
                        return;
                    }
                    baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
//                    sCallMap.remove(getCacheKey(call));
                }

                @Override
                public void onResponse(Call call, Response response) {
                    resloveOnResponse( call, response, baseCallBack );
                    response.body().close();
                    response.close();
//                    sCallMap.remove(getCacheKey(call));
                }
            } );
        }


    }

    @Override
    public void cancleReq(String... urls) {
        for (String url : urls) {
            Call call = sCallMap.get( url );
            if (call != null) {
                call.cancel();
                call = null;
                sCallMap.remove( url );
            }
        }
    }

    @Override
    public void cancleAllRequest() {
        for (Map.Entry entry : sCallMap.entrySet()) {
            ((Call) entry.getValue()).cancel();
        }
        sCallMap.clear();

    }

    /**
     * 设置okhttp 的缓存策略
     *
     * @param application
     */
    private void setCacheStrategy(Application application) {
        //外部存储的缓存路径 application.getExternalCacheDir().getAbsolutePath()
        try {
            File cacheDir = new File( application.getExternalCacheDir().getAbsolutePath() );
            if (cacheDir == null) {
                cacheDir = new File( application.getCacheDir().getAbsolutePath() );
            }
            if (cacheDir != null) {
                //判断路径是否存在
                if (!cacheDir.exists())
                    cacheDir.mkdirs();//路径不存在创建路径
                try {
                    mDiskLruCache = new DiskLruCacheHelper( application, cacheDir, CACHE_SIZE );
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加请求的拦截器，做好数据过滤
     *
     * @param builder
     * @param application
     */
    private void setInterceptStrategy(OkHttpClient.Builder builder, final Application application) {

    }


    /**
     * 处理返回的结果
     *
     * @param call
     * @param response
     * @param baseCallBack
     * @throws IOException
     */
    private static void resloveCacheOnResponse(Call call, Response response, BaseCallBack baseCallBack) throws IOException {
        if (!call.isCanceled()) {
            final String resp = response.body().string();
            String key = getCacheKey( call );
            if (NetStatusUtils.isConnected( CONTEXT )) {
                //如果返回数据为null
                if (TextUtils.isEmpty( resp )) {
                    LogUtil.e( TAG, "网络出错,请检查网络" );
                    LodDialogClass.closeCustomCircleProgressDialog();
                    baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
                } else {
                    if (response.isSuccessful()) {
                        pullData( resp, baseCallBack );
                        if (mDiskLruCache != null) {
                            mDiskLruCache.remove( key );
                            //当需要缓存的数据不为空的时候，并且需要缓存的时候，通过diskLruCacheHelper进行缓存                                   if(!TextUtils
                            // .isEmpty
                            // (key)&&!TextUtils.isEmpty(cacheResponse)&&isCache){
                            mDiskLruCache.put( key, resp );
                            //然后就是回调成功接口
                        }

                    } else {
                        //这个是请求失败，那么就回调失败接口
                        LogUtil.e( TAG, "网络出错,请检查网络" );
                        LodDialogClass.closeCustomCircleProgressDialog();
                        baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
                    }
                }
                return;
            } else {
                if (mDiskLruCache != null) {
                    //没有网络的情况下，去获取key对应的缓存
                    String json = mDiskLruCache.getAsString( key );
                    //如果缓存不存在，那么久回调失败接口
                    LogUtil.e( TAG, "disklrucache:" + json );
                }
            }
        }
    }

    @NonNull
    private static String getCacheKey(Call call) {
        String key = "";
        MediaType contentType = call.request().body().contentType();
        Charset charset = Charset.forName( "UTF-8" );
        if (contentType != null) {
            charset = contentType.charset( Charset.forName( "UTF-8" ) );
        }
        Buffer buffer = new Buffer();
        try {
            call.request().body().writeTo( buffer );
        } catch (IOException e) {
            e.printStackTrace();
        }
        String requestUrl = call.request().url().toString();
        key = requestUrl + buffer.readString( charset );
        key = key.replace( ":", "" ).replace( "//", "" ).replace( ".", "" ).replace( "/", "" ).toLowerCase();
        buffer.close();
        return key;
    }

    /**
     * 将数据转化为对象
     *
     * @param string
     * @param baseCallBack
     * @throws IOException
     */
    private static void pullData(String string, BaseCallBack baseCallBack) throws IOException {
        try {
            JSONObject jsonObject = JSON.parseObject( string );
            ResponseBean responseBean = new ResponseBean();
//            ResponseBean responseBean = JSON.parseObject(string, new TypeReference<ResponseBean>() {});
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                switch (entry.getKey()) {
                    case "code":
                        if (entry.getValue() != null) {
                            responseBean.setCode( entry.getValue().toString() );
                        }
                        break;
                    case "message":
                        if (entry.getValue() != null) {
                            responseBean.setMessage( entry.getValue().toString() );
                        }
                        break;
                    case "data":
                        if (entry.getValue() != null) {
                            responseBean.setData( entry.getValue().toString() );
                        }
                        break;
                }
            }
            if (("200").equals( responseBean.getCode() )) {
                baseCallBack.onSuccess( responseBean );
            } else {
                baseCallBack.onError( responseBean );
            }
        } catch (Exception e) {
            LogUtil.e( TAG, "网络出错,请检查网络" );
            baseCallBack.onErrorForOthers( new ResponseBean( "网络出错,请检查网络", "网络出错,请检查网络", "-1" ) );
        }
    }

    private static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance( "SSL" );
            sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.sslSocketFactory( sslSocketFactory );
            builder.hostnameVerifier( new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            } );

            OkHttpClient okHttpClient = builder.build();
            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException( e );
        }
    }

}
